﻿using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using 节拍计算器2.平台实现;
using 节拍计算器2.资源;

namespace 节拍计算器2
{
    [AddINotifyPropertyChangedInterface]
    public class 节拍点 : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public int 节拍点序号 { get; set; }
        public bool 节拍点亮起 { get; set; }
        public SolidColorBrush 节拍点默认颜色 { get; set; } = new SolidColorBrush(Color.FromArgb(255, 1, 1, 1));
        public SolidColorBrush 节拍点高亮颜色 { get; set; } = new SolidColorBrush(Color.FromArgb(255, 160, 160, 160));

    }

    [AddINotifyPropertyChangedInterface]
    public class 节拍计算器 : INotifyPropertyChanged
    {
        public Task 自动处理线程;

        public string 应用程序名称 { get; set; } = 文本.应用程序名称;

        public string 应用程序主状态 { get; set; } = 文本.初始提示;
        public string 应用程序副状态 { get; set; } = 文本.初始辅助提示;


        public static int 最大点击次数 { get; set; } = 8;
        public static int 精度提升连续阈值 { get; set; } = 20;

        public static int BPM不准确提示高限 { get; set; } = 180;
        public static int BPM不准确提示低限 { get; set; } = 40;
        public static float BPM不准确提示阈值 { get; set; } = 5;

        public bool 显示BPM不准确提示 { get; set; } = false;
        public string BPM不准确提示文字 { get; set; }
        public static Stopwatch 秒表 { get; set; } = new Stopwatch();
        public static Queue<int> 间隔时间点 { get; set; } = new Queue<int>(1);
        public static Dictionary<float, int> BPM权重 { get; set; } = new Dictionary<float, int>();
        public ObservableCollection<节拍点> 节拍点集 { get; set; } = new ObservableCollection<节拍点>() { new 节拍点(), new 节拍点(), new 节拍点(), new 节拍点() };
        public int 节拍总数 { get; set; } = 4;
        public int 节拍量化 { get; set; } = 4;
        /// <summary>
        /// 以0为起始的节拍分量，表示当前用户正按到的节拍分量
        /// </summary>
        public int 当前节拍分量 { get; set; } = 0;
        public float 目前BPM { get; set; } = 0;
        public float 推荐BPM { get; set; } = 0;
        public int BPM精度 { get; set; } = 0;
        public float Offset { get; set; } = 0;
        public int 手动按键延迟 { get; set; } = 1;
        public TimeSpan 每拍秒 { get; set; }
        public int 点击次数 { get; set; }
        public int 误差过大次数 { get; set; }
        public bool 自动处理 { get; set; }
        Timer timer = null;
        public enum 音效类型 { 强拍, 弱拍 }
        public Dictionary<音效类型, 音效> 音效字典 { get; set; }
        public 音频播放 音频 { get; set; }
        public enum _音频状态 { 播放, 暂停, 未加载 }
        public _音频状态 音频状态 { get; set; } = _音频状态.未加载;
        public TimeSpan 音频上次时间 { get; set; } = TimeSpan.Zero;
        public Visibility 播放控制选项状态 { get; set; } = Visibility.Collapsed;
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }


        public 节拍计算器()
        {
            日志.输出("节拍计算器已启动");

            初始化();
        }

        private void 初始化()
        {
            加载音效文件();
            音频 = new Windows音频();
        }

        private void 加载音效文件()
        {
            if (System.IO.File.Exists(应用程序主设置.强拍音效路径) && System.IO.File.Exists(应用程序主设置.弱拍音效路径))
            {
                音效字典 = new Dictionary<音效类型, 音效>
                {
                    { 音效类型.强拍, new Windows音效() { 音效文件路径 = 应用程序主设置.强拍音效路径 } },
                    { 音效类型.弱拍, new Windows音效() { 音效文件路径 = 应用程序主设置.弱拍音效路径 } }
                };
                foreach (var 音效 in 音效字典)
                {
                    音效.Value.加载();
                }
            }
        }

        public async void 追加新的间隔时间点()
        {

            停止自动处理();
            long 间隔时间 = 0;
            if (音频状态 == _音频状态.播放)
            {
                var 本次时间 = 音频.当前播放时间.TotalMilliseconds;
                var 上次时间 = 音频上次时间.TotalMilliseconds;
                音频上次时间 = 音频.当前播放时间;
                if (上次时间 == 0) { 音频上次时间 = 音频.当前播放时间; return; }
                间隔时间 = (long)(本次时间 - 上次时间);
                if (间隔时间 <= 1) { return; }
            }
            else
            {
                秒表.Stop();
                间隔时间 = 秒表.ElapsedMilliseconds - 手动按键延迟;
                秒表.Restart();
            }



            间隔时间点.Enqueue((int)间隔时间);
            if (间隔时间点.Count > 最大点击次数)
            {
                间隔时间点.Dequeue();
            }

            推进节拍点();

            计算并更新BPM();
            计算每拍秒();
            计算Offset();
            更新节拍点();
            自动处理 = false;
            点击次数++;
            UI处理(间隔时间);
        }

        private void UI处理(long 间隔时间)
        {
            bool 是否触发误差 = false;
            if (点击次数 <= 8)
            {
                应用程序主状态 = string.Format(文本.初始测量提示, 9 - 点击次数);
            }
            else if (Math.Abs(间隔时间 - 每拍秒.TotalMilliseconds) > 每拍秒.TotalMilliseconds * 1.5)
            {
                误差过大次数++;
                应用程序主状态 = 文本.误差过大提示;
            }
            else if (Math.Abs(推荐BPM - 目前BPM) > 10)
            {
                应用程序主状态 = 文本.误差过大提示;
                误差过大次数++;
                是否触发误差 = true;
            }
            else if (推荐BPM >= 180)
            {
                应用程序主状态 = 文本.节拍过快提示;
                是否触发误差 = true;
            }
            else
            {
                应用程序主状态 = 文本.空文本;
            }

            误差超限检测(是否触发误差);
        }

        private void 误差超限检测(bool 是否触发误差)
        {
            if (!是否触发误差) { 误差过大次数--; }
            if (误差过大次数 > 16) { 应用程序主状态 = 文本.节拍失误超过最大重试次数提示; 重置(false); }
        }

        private void 推进节拍点()
        {
            if (节拍点集 == null || 节拍点集.Count <= 0)
            {
                return;
            }

            if (点击次数 != 0)
            {
                当前节拍分量++;
            }

            if (当前节拍分量 + 1 > 节拍总数 || 当前节拍分量 == 0)
            {
                if (当前节拍分量 + 1 > 节拍总数)
                {
                    当前节拍分量 = 0;
                }
                播放音效(音效类型.强拍);
            }
            else
            {
                播放音效(音效类型.弱拍);
            }

            //使用MVVM更新会导致极大延迟，此处使用传统的控件指定
            Application.Current.Dispatcher.Invoke(() =>
            {
                MainWindow.运行时.节拍点.SelectedIndex = 当前节拍分量;
            });
        }


        public void 更新节拍点()
        {
            if (节拍点集 == null) { 节拍点集 = new ObservableCollection<节拍点>(); }
            if (节拍点集.Count < 节拍总数) { 节拍点集.Add(new 节拍点() { 节拍点序号 = 节拍点集.Count }); 更新节拍点(); }
            if (节拍点集.Count > 节拍总数) { 节拍点集.RemoveAt(节拍点集.Count - 1); 更新节拍点(); }
        }

        public void 重置(bool 返回主界面 = true)
        {
            if (音频 != null) { 音频.重置(); }

            节拍点集.Clear();
            if (音效字典 != null) { 音效字典.ToList().ForEach(x => { x.Value.销毁(); }); }

            间隔时间点.Clear();
            秒表.Reset();
            停止自动处理();
            目前BPM = 0;
            推荐BPM = 0;
            BPM精度 = 0;
            当前节拍分量 = 0;
            点击次数 = 0;
            音频停止(); 音频状态 = _音频状态.未加载; 播放控制选项状态 = Visibility.Collapsed;
            误差过大次数 = 0;
            BPM权重.Clear();
            节拍点集.Clear();
            更新节拍点();
            自动处理 = false;

            应用程序主状态 = 文本.初始提示;
            if (返回主界面) { 应用程序副状态 = 文本.初始辅助提示; }

        }
        private async void 计算并更新BPM()
        {
            if (间隔时间点 == null || 间隔时间点.Count <= 0) { return; }
            目前BPM = (float)Math.Round(60000 / 间隔时间点.ToList().Average(), BPM精度);
            if (BPM权重.ContainsKey(目前BPM))
            {
                BPM权重[目前BPM] += 1;
            }
            else
            {
                BPM权重.Add(目前BPM, 1);
            }
            var 权重列表 = BPM权重.ToList(); 权重列表.Sort((x, y) => x.Value.CompareTo(y.Value));
            推荐BPM = 权重列表.LastOrDefault().Key;


            if (BPM权重[目前BPM] > 精度提升连续阈值 && BPM精度 <= 3)
            { BPM精度++; BPM权重.Clear(); BPM权重.Add(推荐BPM, 1); }
        }
        private void 计算Offset()
        {
            if (音频 == null) { return; }
            if (string.IsNullOrWhiteSpace(音频.当前播放文件路径)) { return; }
            Offset = (float)(音频.总播放时间.TotalMilliseconds % 每拍秒.TotalMilliseconds);
        }

        private void 计算每拍秒()
        {
            if (推荐BPM == 0) { return; }
            每拍秒 = TimeSpan.FromMilliseconds((double)60000 / 推荐BPM);
        }


        public void 开始自动处理()
        {
            当前节拍分量 = 0;
            自动处理 = true;
            Task.Run(() =>
            {
                var 自动处理节拍数 = 0;
                var stopwatch = new System.Diagnostics.Stopwatch();
                stopwatch.Start();
                while (自动处理)
                {
                    if (音频状态 == _音频状态.播放)
                    {
                        var 本次时间 = 音频.当前播放时间;
                        if (本次时间.Ticks - 音频上次时间.Ticks >= 每拍秒.Ticks)
                        {
                            音频上次时间 = 本次时间;
                            //跳过第一拍
                            if (自动处理节拍数 == 0) { 自动处理节拍数++; continue; }
                            //启动子线程防止因为后台操作延时
                            Task.Run(() => 处理节拍(ref 自动处理节拍数));
                        }
                        continue;
                    }

                    if (stopwatch.ElapsedTicks >= 每拍秒.Ticks)
                    {
                        //跳过第一拍
                        if (自动处理节拍数 == 0) { 自动处理节拍数++; continue; }
                        //启动子线程防止因为后台操作延时
                        Task.Run(() => 处理节拍(ref 自动处理节拍数));
                        stopwatch.Restart();
                    }
                }
                应用程序副状态 = $"";
                stopwatch.Stop();
            });
        }

        private void 处理节拍(ref int 自动处理节拍数)
        {
            推进节拍点();
            应用程序副状态 = string.Format(文本.自动寻拍提示, 当前节拍分量 + 1, 节拍总数, 自动处理节拍数 / 节拍总数, 自动处理节拍数 % 节拍总数 + 1);
            System.Threading.Interlocked.Increment(ref 自动处理节拍数);
        }

        public void 停止自动处理()
        {
            自动处理 = false;

        }

        public enum 微调类型 { 向上, 向下 }
        public void BPM微调(微调类型 类型)
        {
            if (类型 == 微调类型.向上)
            {
                if (推荐BPM >= 999) { 推荐BPM = 999; return; }
                if (BPM精度 == 0) { 推荐BPM += 1; }
                else { 推荐BPM += 1f * (float)Math.Pow(0.1f, BPM精度); }
            }
            if (类型 == 微调类型.向下)
            {
                if (推荐BPM <= 1) { 推荐BPM = 1; return; }
                if (BPM精度 == 0) { 推荐BPM -= 1; }
                else { 推荐BPM -= 1f * (float)Math.Pow(0.1f, BPM精度); }
            }
            计算每拍秒();
            计算Offset();

        }

        public void 打开或更换音频文件(string 文件路径)
        {
            if (音频 == null) { return; }

            音频.重置();
            音频.打开文件(文件路径);

        }

        public void 音频播放()
        {
            if (音频 == null || string.IsNullOrWhiteSpace(音频.当前播放文件路径)) { return; }
            音频状态 = _音频状态.播放;
            音频.播放();
        }

        public void 音频暂停()
        {
            if (音频 == null || string.IsNullOrWhiteSpace(音频.当前播放文件路径)) { return; }
            音频状态 = _音频状态.暂停;
            音频.暂停();
        }

        public void 音频停止()
        {
            if (音频 == null || string.IsNullOrWhiteSpace(音频.当前播放文件路径)) { return; }
            音频.停止();

        }

        public void 播放音效(音效类型 音效)
        {
            Task.Run(() =>
            {
                if (音效字典 == null || 音效字典.Count < 0) { return; }
                音效字典[音效].播放();
            });

        }


    }
}
